# -*- coding: UTF-8 -*-
from abaqus import *
from abaqusConstants import *
import regionToolset
import random

def creatContainer(model_name,width,height,length):
    '''
    功能：生成腔体和匹配的压板

    参数：
    model_name : Model的名称
    width: 腔体的宽度
    height: 腔体的高度
    length: 壳的轴向长度 

    '''
    width *= 1.0
    height *= 1.0 
    length *= 1.0 # 整数转为浮点数
    currModel = mdb.models[model_name]
    currSketch = currModel.ConstrainedSketch(name='container', sheetSize=200.0)
    currSketch.Line(point1 = (0.0,0.0),point2 = (width,0.0))
    currSketch.Line(point1 = (0.0,0.0),point2 = (0.0,height))
    currSketch.Line(point1 = (width,0.0),point2 = (width,height))
    currPart = currModel.Part(name='container', dimensionality=THREE_D, type=ANALYTIC_RIGID_SURFACE)
    currPart.AnalyticRigidSurfExtrude(sketch = currSketch, depth = length)
    # # 创建匹配的压板，其宽度比腔体少2 mm
    # pusher_width = width - 2.0
    # currSketch = currModel.ConstrainedSketch(name='pusher', sheetSize=200.0)
    # currSketch.Line(point1 = (0.0,0.0),point2 = (pusher_width,0.0))
    # currPart = currModel.Part(name = 'pusher', dimensionality=THREE_D, type=ANALYTIC_RIGID_SURFACE)
    # currPart.AnalyticRigidSurfExtrude(sketch = currSketch, depth = length)
    # 给腔体创建接触面
    side1Faces = currPart.faces.findAt(((1e-7,1e-7,0.0),),)
    currPart.Surface(side1Faces = side1Faces, name='Surf-container')
    # 创建参考点
    currPart.ReferencePoint(point = (0.0,0.0,0.0))

def creatOpenshell(model_name,D,openAngle,length):
    '''
    功能：生成开口壳

    参数：
    model_name : Model的名称
    D: 圆壳的直径
    openAngle: 总开口角度(角度制),切下面
    length: 壳的轴向长度 

    '''
    R = D / 2.0
    sideOpenAngle = openAngle * 1.0 / 2.0 # 一侧的开口角度
    length *= 1.0
    currModel = mdb.models[model_name]
    currSketch = currModel.ConstrainedSketch(name='openShell', sheetSize=200.0)
    currSketch.CircleByCenterPerimeter(center=(0.0,0.0), point1=(0.0,R))
    currSketch.ConstructionLine(point1=(0.0, 0.0), angle=sideOpenAngle) # 带角度的辅助线
    currSketch.ConstructionLine(point1=(0.0, 0.0), angle= -1.0 * sideOpenAngle)
    currSketch.autoTrimCurve(curve1=currSketch.geometry.findAt((0,R),), point1=(0, -1.0 * R)) # point1选中下面的弧段
    currPart = currModel.Part(name='openShell', dimensionality=THREE_D, type=DEFORMABLE_BODY)
    currPart.BaseShellExtrude(sketch=currSketch, depth=length)

def assembly2ROS(model_name,gap,perRowNum,totalNum):
    '''
    功能：向规则格子中布置随机开口朝向的开口壳

    参数：
    model_name : Model的名称
    gap: 相邻开口壳之间的距离，计算方法为 直径+间隙
    perRowNum: 每层放置的开口壳数量
    totalNum: 要布置的开口壳总数量 
    '''
    gap *= 1.0
    currModel = mdb.models[model_name]
    currAssembly = currModel.rootAssembly
    currAssembly.DatumCsysByDefault(CARTESIAN)
    openShellPart = currModel.parts['openShell']
    # 准备做开口壳的插入工作
    for i in  range(totalNum):
        newInstanceName = 'openShell-'+str(i)
        rotAngel = random.randint(0, 360) # 计算随机角度,这是生成随机整数的函数
        grid_x = i % perRowNum
        grid_y = i // perRowNum # 计算应该放置到格子的位置
        offset_x = grid_x * gap
        offset_y = grid_y * gap # 计算位移距离
        currAssembly.Instance(name=newInstanceName, part=openShellPart, dependent=OFF)
        currAssembly.rotate(instanceList=(newInstanceName, ), axisPoint=(0.0, 0.0, 0.0), axisDirection=(0.0, 0.0, 10.0), angle=rotAngel)
        currAssembly.translate(instanceList=(newInstanceName, ), vector=(offset_x, offset_y, 0.0))

def assemblyContainer2ROS(model_name,offset_x,offset_y,length):
    '''
    功能：添加容器至装配体
    参数：
    model_name : Model的名称
    offset_x: x方向移动距离
    offset_y: y方向移动距离
    height: 容器的纵向长度
    '''
    offset_x *= 1.0
    offset_y *= 1.0
    length *= 1.0
    currModel = mdb.models[model_name]
    currAssembly = currModel.rootAssembly
    containerPart = currModel.parts['container']
    # pusherPart = currModel.parts['pusher']
    currAssembly.Instance(name='container', part=containerPart, dependent=OFF)
    # currAssembly.Instance(name='pusher', part=pusherPart, dependent=OFF)
    currAssembly.translate(instanceList=('container', ), vector=(offset_x, offset_y, length/2.0))


# session.viewports['Viewport: 1'].assemblyDisplay.setValues(renderStyle=SHADED)

# test:
# creatRSSJob('myscript','my1_0',335,400,10,50,90,1,55,6,30,100,0.5)

def creatRSSJob(model_name,job_name,width,height,length,D,openAngle,shellThickness,gap,perRowNum,totalNum,drop_height,compute_time):
    '''
    功能：程序主入口，生成一个任务

    参数：
    model_name : Model的名称
    job_name: 任务的名称
    width: 腔体的宽度
    height: 腔体的高度
    length: 壳的轴向长度
    D: 圆壳的直径
    openAngle: 总开口角度(角度制),切下面
    shellThickness: 壳体截面的厚度，材料模块
    gap: 相邻开口壳之间的距离，计算方法为 直径+间隙
    perRowNum: 每层放置的开口壳数量
    totalNum: 要布置的开口壳总数量
    drop_height: 掉落的高度
    compute_time: 计算的时间，推荐范围0~1
    '''

    width *= 1.0
    height *= 1.0
    length *= 1.0
    D *= 1.0
    openAngle *= 1.0
    gap *= 1.0
    shellThickness *= 1.0
    drop_height *= 1.0
    compute_time *= 1.0
    
    currModel = mdb.Model(name = model_name, modelType = STANDARD_EXPLICIT)

    import sketch
    import part
    creatContainer(model_name,width,height,length)
    creatOpenshell(model_name,D,openAngle,length)
    part_container = currModel.parts['container']
    part_openShell = currModel.parts['openShell']

    import assembly
    assembly2ROS(model_name,gap,perRowNum,totalNum)
    offset_x = - D / 2.0 - (width - (D + gap * (perRowNum - 1))) / 2.0
    offset_y = - D / 2.0 - drop_height
    assemblyContainer2ROS(model_name,offset_x,offset_y,length)
    currAssembly = currModel.rootAssembly

    import material
    mat01_name = 'AISI 1005 Steel'
    mat01 = currModel.Material(name = mat01_name)
    mat01.Density(table = ((7.8e-9, ), ))
    mat01.Elastic(table = ((200E3 , 0.29), ))

    import section
    shellSection = currModel.HomogeneousShellSection(name='shellSection', material=mat01_name, thicknessType=UNIFORM, thickness=shellThickness)
    part_openShell.SectionAssignment(region=(part_openShell.faces,), sectionName='shellSection', offset=0.0, offsetType=MIDDLE_SURFACE, offsetField='', thicknessAssignment=FROM_SECTION)

    import step 
    currModel.ExplicitDynamicsStep(name='drop', previous='Initial', timePeriod=compute_time, improvedDtMethod=ON)

    import interaction
    currModel.ContactProperty('IntProp-1')
    currModel.interactionProperties['IntProp-1'].TangentialBehavior(formulation=PENALTY, directionality=ISOTROPIC, slipRateDependency=OFF, pressureDependency=OFF, temperatureDependency=OFF, dependencies=0, table=((0.2, ), ), shearStressLimit=None, maximumElasticSlip=FRACTION, fraction=0.005, elasticSlipStiffness=None)
    currModel.interactionProperties['IntProp-1'].NormalBehavior(pressureOverclosure=HARD, allowSeparation=ON, constraintEnforcementMethod=DEFAULT)

    currModel.ContactExp(name='Int-1', createStepName='Initial')
    currModel.interactions['Int-1'].includedPairs.setValuesInStep(stepName='Initial', useAllstar=ON)
    currModel.interactions['Int-1'].contactPropertyAssignments.appendInStep(stepName='Initial', assignments=((GLOBAL, SELF, 'IntProp-1'), ))

    import load
    currModel.Gravity(name='gravity', createStepName='drop', comp2=-9800.0, distributionType=UNIFORM, field='') # 对全体施加重力
    # 限制所有壳体的面外位移自由度
    region_faces = currAssembly.instances['openShell-0'].faces
    region_edges = currAssembly.instances['openShell-0'].edges
    region_vertices = currAssembly.instances['openShell-0'].vertices
    for i in range(1,totalNum):
        shell_instance_name = 'openShell' + '-' + str(i)
        iter_instance = currAssembly.instances[shell_instance_name]
        region_faces += iter_instance.faces
        region_edges += iter_instance.edges
        region_vertices += iter_instance.vertices
    region_all = regionToolset.Region(vertices = region_vertices, edges = region_edges, faces = region_faces)
    currModel.DisplacementBC(name='withoutZ', createStepName='Initial', region=region_all, u1=UNSET, u2=UNSET, u3=0.0, ur1=UNSET, ur2=UNSET, ur3=UNSET, amplitude=UNSET, fixed=OFF, distributionType=UNIFORM, fieldName='', localCsys=None)
    # 对腔体施加固定约束,注意：！！！！参考点是字典！！！！！
    ref_region = regionToolset.Region(referencePoints=(currAssembly.instances['container'].referencePoints.values(),))
    currModel.EncastreBC(name = 'fixed', createStepName='Initial', region=ref_region, localCsys=None)

    import mesh
    List_mesh_instance = list()
    for i in range(totalNum):
        shell_instance_name = 'openShell' + '-' + str(i)
        List_mesh_instance.append(currAssembly.instances[shell_instance_name])
    Tuple_mesh_instance = tuple(List_mesh_instance)
    mesh_size = floor(length * 0.2) # meshsize————全局网格密度，最大取轴向长度的20%
    currAssembly.seedPartInstance(regions=Tuple_mesh_instance, size=mesh_size, deviationFactor=0.1, minSizeFactor=0.1)
    currAssembly.generateMesh(regions=Tuple_mesh_instance)

    import job 
    #-------------此部分提交任务并进行检查--------------
    session.viewports['Viewport: 1'].assemblyDisplay.setValues(renderStyle=SHADED)
    session.viewports['Viewport: 1'].viewportAnnotationOptions.setValues(triad=ON, title=ON, state=ON, annotations=ON, compass=ON)
    session.viewports['Viewport: 1'].setValues(displayedObject = currAssembly)

    mdb.Job(name=job_name, model=currModel, description='', type=ANALYSIS, atTime=None, waitMinutes=0, waitHours=0, queue=None, memory=90, memoryUnits=PERCENTAGE, explicitPrecision=DOUBLE_PLUS_PACK, nodalOutputPrecision=SINGLE, echoPrint=OFF, modelPrint=OFF, contactPrint=OFF, historyPrint=OFF, userSubroutine='', scratch='', resultsFormat=ODB, parallelizationMethodExplicit=DOMAIN, numDomains=6, activateLoadBalancing=False, multiprocessingMode=DEFAULT, numCpus=6)
    mdb.jobs[job_name].submit(consistencyChecking=OFF, datacheckJob=True)


    